package com.mlming.springboot.service.impl;

import com.google.gson.Gson;
import com.mlming.springboot.consts.MallConst;
import com.mlming.springboot.dao.ProductMapper;
import com.mlming.springboot.enums.ProductStatusEnum;
import com.mlming.springboot.enums.ResponseEnum;
import com.mlming.springboot.form.CartAddForm;
import com.mlming.springboot.form.CartUpdateForm;
import com.mlming.springboot.pojo.Cart;
import com.mlming.springboot.pojo.Product;
import com.mlming.springboot.service.CartService;
import com.mlming.springboot.vo.CartProductVo;
import com.mlming.springboot.vo.CartVo;
import com.mlming.springboot.vo.ResponseVo;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private ProductMapper productMapper;// 用于查询商品数据
    @Autowired
    private RedisTemplate redisTemplate; // redis依赖提供的内置dao层对象

    private Gson gson = new Gson(); // 用于json与Cart对象之间的转换

    @Override
    public ResponseVo<CartVo> addCartItem(Integer userId, CartAddForm cartAddForm) {
        Integer productId = cartAddForm.getProductId();// 商品id
        // 通过productId从商品表查出该商品
        Product product = productMapper.selectByPrimaryKey(productId);
        // 判断该商品存在不存在,不存在就报错误信息
        if(product == null) {
            return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXISTS);
        }
        // 判断该商品是否在售:
        if(product.getStatus().equals(ProductStatusEnum.OFF_SALE.getCode()) || product.getStatus().equals(ProductStatusEnum.DELETE.getCode())) {
            return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE);
        }
        // 判断该商品库存是否足够
        if(product.getStock() <= 0) {
            return ResponseVo.error(ResponseEnum.PRODUCT_STOCK_NOT_ENOUGH);
        }
        // 以上判断均成功后, 开始往Redis写入数据:
        // 选取的Redis数据类型:
        // 由于Redis要存多用户,所以必须得是以userId作为key
        // 同时又因为每个用户,即每个key对应的value,即购物车,必须是要有多个Cart类对象的
        // 所以应该采用的是hash数据类型: 因为hash类型对应的是 对象,所以下面以对象的形式解释形式
        // 对象名(key): 用户id相关的唯一标识
        // 属性列:
        //  属性名: productId唯一标识
        //  属性值: Cart类转json格式 => 主要是为了方便存储,如果这里要存对象,Redis没有对应的数据类型
        // 即: **以对象的形式存一个购物车, 属性列的形式存储购物车里面的多个Cart**
        // 利用Redis对hash数据类型的添加操作的机制:
        //      如果不存在key,则新建一个;如果key存在,则会覆盖已存在的属性名的属性值,并新建不存在的属性名
        // 所以根据这个机制,**我们无论是添加还是修改,都只需要put往redis里添加数据就行了**
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        // 首先得查出该Cart是否存在:
        String value = hashOperations.get(userId, productId);
        // 如果不存在,则新建一个Cart类
        Cart cart;
        if(StringUtils.isNullOrEmpty(value)) {
            cart = new Cart(productId, MallConst.CART_INIT_QUANTITY,cartAddForm.getSelected());
        }
        // 如果存在的话,根据接口文档,是在原有数量的基础上+1作为新数量
        else {
            cart = gson.fromJson(value, Cart.class);
            cart.setQuantity(cart.getQuantity()+1);
        }
        cart.setProductSelected(cartAddForm.getSelected());
        // 处理结束后,根据上述的存储格式规定, 写入redis
        hashOperations.put(userId,productId,gson.toJson(cart));

        return getCartList(userId);
    }

    @Override
    public ResponseVo<CartVo> getCartList(Integer userId) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        // 从Redis中查询出key==userId的value => 购物车的 所有属性列表 => 每个购物车记录
        Map<Integer, String> entries = hashOperations.entries(userId);
        // 获取到所有的商品id,并从商品表中查询商品列表
        Set<Integer> productSet = entries.keySet();
        // 如果商品id为空, 则直接返回空值, 否则的话下面那个查询用的是in,由于集合是空的,所以会直接返回所有Product,不符合逻辑
        if(CollectionUtils.isEmpty(productSet)) {
            CartVo cartVo = new CartVo(new ArrayList<>(),false,BigDecimal.ZERO,0);
            return ResponseVo.success(cartVo);
        }
        List<Product> products = productMapper.selectByProductIdSet(productSet);
        // 遍历所有的商品,装载到要返回的CartProductVo类里面
        // 并把CartProductVo类加入到cartProductVoList,
        // 中间要业务性地求出CartVo的其他两个字段cartTotalPrice,selectedAll,cartTotalQuantity
        List<CartProductVo> cartProductVoList = new ArrayList<>(); // vo商品列表
        Boolean selectedAll = true;// 全选
        BigDecimal cartTotalPrice = BigDecimal.ZERO;// 总价格
        Integer cartTotalQuantity = 0;// 总数
        for (Product product : products) {
            // 通过productId取出entries中的当前对应的Cart
            Cart curCart = gson.fromJson(entries.get(product.getId()),Cart.class);
            CartProductVo cartProductVo = new CartProductVo();
            cartProductVo.setProductId(product.getId());
            cartProductVo.setQuantity(curCart.getQuantity());
            cartProductVo.setProductName(product.getName());
            cartProductVo.setProductSubtitle(product.getSubtitle());
            cartProductVo.setProductMainImage(product.getMainImage());
            cartProductVo.setProductPrice(product.getPrice());
            cartProductVo.setProductStatus(product.getStatus());
            cartProductVo.setProductStock(product.getStock());
            cartProductVo.setProductTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(curCart.getQuantity())));
            cartProductVo.setProductSelected(curCart.getProductSelected());
            // CartVo类的参数修改:
            // 注意这里有业务要求: 结合现实的购物车
            // 关于在购物车里面的商品是否还在售的状态,即status:
            //      商品列表不受影响,无论在售与否都要返回给前端显示
            //      商品数量也不受影响,同上理
            //      商品是否是全选, 由于不在售的商品没有选择可言,所以不能影响商品的全选属性
            //      商品的总价格,肯定是求在售的商品的
            cartProductVoList.add(cartProductVo);
            cartTotalQuantity = cartTotalQuantity + cartProductVo.getQuantity();
            if(cartProductVo.getProductStatus().equals(ProductStatusEnum.ON_SALE.getCode()) && cartProductVo.getProductSelected() == false) {
                selectedAll = false;
            }
            // 业务要求: 总价格只求被选中的
            if(cartProductVo.getProductStatus().equals(ProductStatusEnum.ON_SALE.getCode()) && cartProductVo.getProductSelected()) {
                cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductTotalPrice());
            }
        }
        // 遍历结束后,设置CartVo作为返回的data
        CartVo cartVo = new CartVo(cartProductVoList,selectedAll,cartTotalPrice,cartTotalQuantity);

        return ResponseVo.success(cartVo);
    }

    @Override
    public ResponseVo<CartVo> updateCartItem(Integer userId,Integer productId,CartUpdateForm cartUpdateForm) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        String value = hashOperations.get(userId, productId);
        // 如果redis原来不存在该商品,则报错
        if(StringUtils.isNullOrEmpty(value)) {
            return ResponseVo.error(ResponseEnum.CART_NOT_EXISTS);
        }
        // 如果存在,修改其内容
        Cart cart = gson.fromJson(value,Cart.class);
        if(cartUpdateForm.getQuantity() != null && cartUpdateForm.getQuantity() >= 0) {
            cart.setQuantity(cartUpdateForm.getQuantity());
        }
        if(cartUpdateForm.getSelected() != null) {
            cart.setProductSelected(cartUpdateForm.getSelected());
        }
        // 写入redis数据库
        hashOperations.put(userId,productId,gson.toJson(cart));

        return getCartList(userId);
    }

    @Override
    public ResponseVo<CartVo> deleteCartItem(Integer userId, Integer productId) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        String value = hashOperations.get(userId, productId);
        // 如果redis原来不存在该商品,则报错
        if(StringUtils.isNullOrEmpty(value)) {
            return ResponseVo.error(ResponseEnum.CART_NOT_EXISTS);
        }
        // 如果存在,删除
        hashOperations.delete(userId,productId);

        return getCartList(userId);
    }

    @Override
    public ResponseVo<CartVo> selectAll(Integer userId) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        for(Cart cart : getCarts(userId)) {
            cart.setProductSelected(true);
            hashOperations.put(userId,cart.getProductId(),gson.toJson(cart));
        }
        return getCartList(userId);
    }

    @Override
    public ResponseVo<CartVo> unSelectAll(Integer userId) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        for(Cart cart : getCarts(userId)) {
            cart.setProductSelected(false);
            hashOperations.put(userId,cart.getProductId(),gson.toJson(cart));
        }
        return getCartList(userId);
    }

    @Override
    public ResponseVo<Integer> sum(Integer userId) {
        Integer totalQuantity = 0;
        for(Cart cart : getCarts(userId)) {
            totalQuantity = totalQuantity + cart.getQuantity();
        }
        return ResponseVo.success(totalQuantity);
    }

    // 查询出所有的购物车内所有Cart列表
    public List<Cart> getCarts(Integer userId) {
        HashOperations<Integer, Integer, String> hashOperations = redisTemplate.opsForHash();
        Map<Integer, String> entries = hashOperations.entries(userId);
        List<Cart> list = new ArrayList<>();
        for(Map.Entry<Integer,String> entry : entries.entrySet()) {
            list.add(gson.fromJson(entry.getValue(),Cart.class));
        }
        return list;
    }

}
